﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ZPod.MTG.Communication;
using ZPod.MTG.Communication.Web;
using System.Diagnostics;
using System.Configuration;

namespace ZPod.MTG.Windows
{
	public partial class ConnectionForm : Form
	{
		#region Private properties
		string _server;
		string _game;
		string _nick;
		
		bool _myHandOpen = false;
		bool _conectado = false;

		PlayerState _currentPlayerState;
		IClient _client;
		Dictionary<string, PlayerDialogUpdater> _updater = new Dictionary<string,PlayerDialogUpdater>();

		#endregion

		#region Constants
		private const int UpdateInterval = 2000;
		private const string DefaultServerAddress = "http://localhost:1234/GameServer";
		#endregion

		#region Public Constructors
		public ConnectionForm()
		{
			InitializeComponent();

			textServer.Text = GetDefaultAddress();
		}
		#endregion

		#region Private Methods

		#region Client

		IClient GetClient()
		{
			try
			{
				if (_client != null && _server != textServer.Text)
				{
					_client.Dispose();
					_client = null;
				}

				if (_client == null)
				{
					
					Uri server = new Uri(textServer.Text);
					string connectionString = String.Format("{0}|{1}|{2}",
						server.Host,
						server.Port,
						server.PathAndQuery);

					_client = GetClientImplementation();
					_client.ConnectionString = connectionString;
					_client.Connect();

					_server = textServer.Text;
				}

				return _client;
			}
			catch (Exception ex)
			{
				MessageBox.Show("Hubo un problema al conectarse con el servidor", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				LogException(ex);
				throw;
			}
		}


		private string GetDefaultAddress()
		{
			try
			{
				string address= ConfigurationManager.AppSettings["DefaultAddress"];
				if (!String.IsNullOrEmpty(address))
				{
					return address;
				}
				else
				{
					return DefaultServerAddress;
				}
			}
			catch
			{
				return DefaultServerAddress;
			}
		}

		private IClient GetClientImplementation()
		{
			try
			{
				string clientType = ConfigurationManager.AppSettings["ClientType"];

				if (!String.IsNullOrEmpty(clientType))
				{
					IClient client = (IClient)Activator.CreateInstance(Type.GetType(clientType, true));
					return client;
				}
				else
				{
					return new MTG.Communication.Web.Client();
				}
			}
			catch
			{
				return new MTG.Communication.Web.Client();
			}
		}

		#endregion

		#region Control Event Handlers

		void buttonUpdate_Click(object sender, EventArgs e)
		{
			try
			{
				var client = GetClient();
				comboGame.DataSource = client.AvailableGames;
			}
			catch (Exception ex)
			{
				LogException(ex);
			}
		}

		void buttonCreate_Click(object sender, EventArgs e)
		{
			IClient client = null;
			try
			{
				client = GetClient();
				if (!client.AvailableGames.Contains(comboGame.Text))
				{
					client.CreateGame(comboGame.Text);
				}
			}
			catch (Exception ex)
			{
				LogException(ex);
			}
		
		}

		void buttonConnect_Click(object sender, EventArgs e)
		{
			IClient client = null;
			try
			{
				client = GetClient();
				if (client.AvailableGames.Contains(comboGame.Text))
				{
					_game = comboGame.Text;
					client.CurrentGame = _game;

					_nick = textNick.Text;
					client.PlayerName = _nick;

					var game = client.CurrentState;
					Deck deck = null;

					if (game.PlayerList.Contains(textNick.Text))
					{
						_currentPlayerState = game.GetState(textNick.Text);
						deck = _currentPlayerState.Deck;
					}
					else
					{
						game.PlayerList.Add(textNick.Text);

						if (String.IsNullOrEmpty(textNick.Text))
						{
							return;
						}

						try
						{
							deck = new Deck(textDeckPath.Text);
						}
						catch
						{
							MessageBox.Show("No se puede leer el mazo", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
							throw;
						}

						_currentPlayerState = new PlayerState()
						{
							Date = DateTime.Now,
							Creatures = new List<Pair<Card, bool>>(),
							General = new List<Pair<Card, bool>>(),
							Graveyard = new List<Card>(),
							Hand = new List<Card>(),
							Mana = new List<Pair<Card, bool>>(),
							Deck = deck
						};

						client.SetPlayerState(_currentPlayerState);
					}

					_conectado = true;

					UpdateControls();
				}
				else
				{
					MessageBox.Show("La partida no existe", "Información", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
			}
			catch (Exception ex)
			{
				LogException(ex);
			}
		
		}

		void buttonSearchDeck_Click(object sender, EventArgs e)
		{
			if (deckDialog.ShowDialog() == DialogResult.OK)
			{
				textDeckPath.Text = deckDialog.FileName;
			}
		}

		void buttonOpponent_Click(object sender, EventArgs e)
		{
			if (comboPlayers.SelectedIndex >= 0)
			{
				var player = comboPlayers.Text;
				var game = GetClient().CurrentState;
				if (!_updater.ContainsKey(player) && game.PlayerList.Count > 1)
				{
					var opponentHand = new PlayerDialog();
					opponentHand.Text = player;
					opponentHand.CanControl = false;
					opponentHand.MdiParent = this.MdiParent;
					opponentHand.Show();

					_updater[player] = new PlayerDialogUpdater(UpdateInterval, opponentHand, player, this);
				}
			}
		}

		void buttonMyHand_Click(object sender, EventArgs e)
		{
			if (!_myHandOpen)
			{
				IClient client = null;
				try
				{
					client = GetClient();
					//recuperar el estado del server
					var game = client.CurrentState;
					_currentPlayerState = game.GetState(_nick);

					var myHand = new PlayerDialog();
					myHand.CanControl = true;
					myHand.State = _currentPlayerState;
					myHand.Text = _nick;
					myHand.StateChanged += new EventHandler(myHand_StateChanged);
					_myHandOpen = true;
					myHand.FormClosed += new FormClosedEventHandler(myHand_FormClosed);
					myHand.MdiParent = this.MdiParent;
					myHand.Show();
				}
				catch (Exception ex)
				{
					MessageBox.Show("La jugada quedará inválida", "Informacion", MessageBoxButtons.OK, MessageBoxIcon.Information);
					LogException(ex);
				}
			}
		}
		
		void buttonCurrentCard_Click(object sender, EventArgs e)
		{
			CurrentCardDialog.ShowCurrentCardDialog(this.MdiParent);
		}

		private void comboPlayers_DropDown(object sender, EventArgs e)
		{
			try
			{
				var client = GetClient();
				comboPlayers.DataSource = client.CurrentState.PlayerList.Where(s => s != _nick).ToList();
			}
			catch (Exception ex)
			{
				LogException(ex);
			}
		}

		#endregion

		#region PlayerDialog Event Handlers
		
		void myHand_FormClosed(object sender, FormClosedEventArgs e)
		{
			_myHandOpen = false;
		}

		void myHand_StateChanged(object sender, EventArgs e)
		{
			IClient client = null;
			try
			{
				client = GetClient();
				PlayerDialog dialog = (PlayerDialog)sender;
				var newState = dialog.State;
				client.SetPlayerState(newState);

				_currentPlayerState = newState;
			}
			catch (Exception ex)
			{
				MessageBox.Show("La jugada quedará inválida", "Informacion", MessageBoxButtons.OK, MessageBoxIcon.Information);
				LogException(ex);
			}

		}

		#endregion

		#region Support Methods

		void UpdateControls()
		{
			buttonCreate.Enabled = buttonUpdate.Enabled = buttonConnect.Enabled = !_conectado;
			textDeckPath.Enabled = textNick.Enabled = textServer.Enabled = comboGame.Enabled = !_conectado;
			comboPlayers.Enabled = buttonCurrentCard.Enabled = buttonMyHand.Enabled = buttonOpponent.Enabled = _conectado;
		}

		static void LogException(Exception ex)
		{
			string message = ex.Message + Environment.NewLine + ex.StackTrace;
			Console.Write(message);
			EventLog.WriteEntry("MTGDeck", message, EventLogEntryType.Error);
		}

		#endregion

		#endregion

		#region Support Classes

		class PlayerDialogUpdater
		{
			private Timer _timer;
			private PlayerDialog _dialog;
			private ConnectionForm _caller;
			private DateTime _lastUpdate = DateTime.MinValue;
			private string _player;

			public PlayerDialogUpdater(int interval, PlayerDialog dialog, string player, ConnectionForm caller)
			{
				_timer = new Timer();
				_timer.Interval = interval;
				_dialog = dialog;
				_player = player;
				_caller = caller;

				dialog.FormClosed += new FormClosedEventHandler(dialog_FormClosed);
				_timer.Tick += new EventHandler(Timer_Tick);
				_timer.Enabled = true;
			}

			void Timer_Tick(object sender, EventArgs e)
			{
				try
				{
					var game = _caller._client.CurrentState;
					var playerState = game.GetState(_player);

					if (_lastUpdate < playerState.Date)
					{
						_lastUpdate = playerState.Date;
						_dialog.State = playerState;
					}
				}
				catch (Exception ex)
				{
					LogException(ex);
				}
			}

			void dialog_FormClosed(object sender, FormClosedEventArgs e)
			{
				_timer.Enabled = false;
				_timer.Dispose();
				_timer = null;
				_dialog.Dispose();
				_dialog = null;
				_caller._updater.Remove(_player);
			}
		}

		#endregion
	}
}
